﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MetalSoda
{
    /// <summary>
    /// Provides support for the fault-tolerant lazy initialization.
    /// </summary>
    /// <typeparam name="T">Specifies the type of object that is being lazily initialized.</typeparam>
    [Serializable]
    sealed class LazyObject<T> where T : class
    {
        #region Private Fields
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private T m_object;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private Func<T> m_objectFactor; 
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="LazyObject{T}"/> class. 
        /// </summary>
        /// <param name="objectFactory">The delegate that is invoked to produce the lazily initialized value when it is needed.</param>
        public LazyObject(Func<T> objectFactory)
        {
            if (objectFactory == null) { throw new ArgumentNullException("objectFactory"); }
            m_objectFactor = objectFactory;
        }

        /// <summary>
        /// Gets the lazily initialized value of the current <see cref="LazyObject{T}"/> instance.
        /// </summary>
        public T Value
        {
            [DebuggerStepThrough]
            get 
            {
                if (m_objectFactor == null) { return m_object; }
                try 
                {
                    m_object = m_objectFactor();
                    m_objectFactor = null;
                    return m_object;
                }
                catch 
                { 
                    m_object = null; 
                    throw; 
                }
            }
        }

        /// <summary>
        /// Gets a value that indicates whether a value has been created for this <see cref="LazyObject{T}"/> instance.
        /// </summary>
        public bool IsValueCreated 
        {
            [DebuggerStepThrough]
            get { return System.Object.ReferenceEquals(m_objectFactor, null); }
        }
    }
}
